﻿using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Color=Microsoft.Xna.Framework.Graphics.Color;

namespace GP2D3D_Homework1_RickBeijer_S1012261
{
    /// <summary>
    /// Indicates which viewpoint has to be used or is currently used by the graphics object.
    /// </summary>
    public enum ViewPoint
    {
        TwoD,
        ThreeD
    };

    public class Graphics
    {
        #region Properties
        private GraphicsDevice graphicsDevice;
        private float width;
        private float height;
        private float depth;
        private BasicEffect basicEffect;
        private ViewPoint currentViewPoint;
        private float viewRotation;
        private List<VertexPositionColor> points;
        private List<Vector3> pointsCheckList;
        private PrimitiveType pointDrawStyle;
        private List<VertexPositionColor[]> cubes;
        private List<LocationDimensionPair> cubesCheckList;
        private readonly IndexBuffer cubeIndexBuffer;
        private PrimitiveType cubeDrawStyle;
        private List<VertexPositionColor[]> pyramids;
        private List<LocationDimensionPair> pyramidsCheckList;
        private readonly IndexBuffer pyramidIndexBuffer;
        private PrimitiveType pyramidDrawStyle;

        public GraphicsDevice GraphicsDevice
        {
            get { return graphicsDevice; }
            set { graphicsDevice = value; }
        }

        public float Width
        {
            get { return width; }
            set { width = value; }
        }

        public float Height
        {
            get { return height; }
            set { height = value; }
        }

        public float Depth
        {
            get { return depth; }
            set { depth = value; }
        }

        public BasicEffect BasicEffect
        {
            get { return basicEffect; }
            set { basicEffect = value; }
        }

        public ViewPoint CurrentViewPoint
        {
            get { return currentViewPoint; }
            set
            {
                if (value == ViewPoint.TwoD)
                {
                    SetBasic2DViewPoint();
                }
                else if (value == ViewPoint.ThreeD)
                {
                    SetBasic3DViewPoint();
                }

                currentViewPoint = value;
            }
        }

        /// <summary>
        /// The ViewRotation of the 3D view, in degrees. When set, it checks if the given float is not
        /// out of the normal degree bounds (0 to 360).
        /// </summary>
        public float ViewRotation
        {
            get { return viewRotation; }
            set
            {
                viewRotation = value;

                if (viewRotation < 0)
                {
                    viewRotation += 360;
                }
                else if (viewRotation > 360)
                {
                    viewRotation -= 360;
                }
            }
        }

        public PrimitiveType PointDrawStyle
        {
            get { return pointDrawStyle; }
            set { pointDrawStyle = value; }
        }

        public PrimitiveType CubeDrawStyle
        {
            get { return cubeDrawStyle; }
            set
            {
                if (value != PrimitiveType.PointList)
                {
                    cubeDrawStyle = value;
                }
            }
        }

        public PrimitiveType PyramidDrawStyle
        {
            get { return pyramidDrawStyle; }
            set
            {
                if (value != PrimitiveType.PointList)
                {
                    pyramidDrawStyle = value;
                }
            }
        }
        #endregion

        #region Constructors
        public Graphics(GraphicsDevice graphicsDevice, float width, float height, float depth)
        {
            this.graphicsDevice = graphicsDevice;
            this.width = width;
            this.height = height;
            this.depth = depth;
            this.basicEffect = new BasicEffect( this.graphicsDevice, null );

            this.points = new List<VertexPositionColor>();
            this.pointsCheckList = new List<Vector3>();
            this.pointDrawStyle = PrimitiveType.PointList;
            this.cubes = new List<VertexPositionColor[]>();
            this.cubesCheckList = new List<LocationDimensionPair>();

            short[] cubeIndices = new short[36]
                                      {
                                              4, 5, 7,
                                              7, 5, 6,
                                              6, 2, 7,
                                              7, 2, 3,
                                              3, 2, 0,
                                              0, 2, 1,
                                              1, 2, 6,
                                              6, 5, 1,
                                              1, 5, 0,
                                              0, 5, 4,
                                              4, 7, 0,
                                              0, 7, 3
                                      };

            cubeIndexBuffer = new IndexBuffer( GraphicsDevice,
                                               cubeIndices.Length * sizeof(short),
                                               BufferUsage.WriteOnly,
                                               IndexElementSize.SixteenBits );
            cubeIndexBuffer.SetData<short>( cubeIndices );
            this.cubeDrawStyle = PrimitiveType.TriangleList;

            this.pyramids = new List<VertexPositionColor[]>();
            this.pyramidsCheckList = new List<LocationDimensionPair>();

            short[] pyramidIndices = new short[18]
                                         {
                                                 0, 1, 4,
                                                 1, 2, 4,
                                                 2, 3, 4,
                                                 3, 0, 4,
                                                 1, 2, 0,
                                                 3, 2, 0
                                         };

            pyramidIndexBuffer = new IndexBuffer( GraphicsDevice,
                                                  pyramidIndices.Length * sizeof(short),
                                                  BufferUsage.WriteOnly,
                                                  IndexElementSize.SixteenBits );
            pyramidIndexBuffer.SetData<short>( pyramidIndices );
            this.pyramidDrawStyle = PrimitiveType.TriangleList;
        }
        #endregion

        #region ViewPoint
        /// <summary>
        /// Set the viewpoint of the graphics to a 2D, front view. The center of the level is point 0, 0, 0. 
        /// When adding points to the graphics, subtract Width / 2 from the X and Height / 2 from the Y.
        /// </summary>
        public void SetBasic2DViewPoint()
        {
            if (basicEffect != null)
            {
                basicEffect.World = Matrix.Identity;
                basicEffect.View = new Matrix( 1.0f, 0.0f,  0.0f, 0.0f,
                                               0.0f, 1.0f,  0.0f, 0.0f,
                                               0.0f, 0.0f, -1.0f, 0.0f,
                                               0.0f, 0.0f,  0.0f, 1.0f );

                ResetProjection2D();
            }
        }

        /// <summary>
        /// Reset the projection of the 2D viewpoint. Use this when the size of the graphics/backbuffer has
        /// changed (the properties Width, Height and Depth will NOT call this method).
        /// </summary>
        public void ResetProjection2D()
        {
            if (basicEffect != null)
            {
                basicEffect.Projection = Matrix.CreateOrthographicOffCenter(-(width / 2), (width / 2), (height / 2), -(height / 2), -depth, depth);
            }
        }

        /// <summary>
        /// Set the viewpoint of the graphics to a 3D, slightly elevated front view. This will make the
        /// graphics content rotate around it's Y axis (Y = 0).
        /// </summary>
        public void SetBasic3DViewPoint()
        {
            if (basicEffect != null)
            {
                basicEffect.World = Matrix.CreateRotationY(MathHelper.ToRadians(viewRotation));

                ResetProjection3D();
            }
        }

        /// <summary>
        /// Reset the projection of the 2D viewpoint. Use this when the size of the graphics/backbuffer has
        /// changed (the properties Width, Height and Depth will NOT call this method).
        /// </summary>
        public void ResetProjection3D()
        {
            if (basicEffect != null)
            {
                basicEffect.View = Matrix.CreateLookAt(new Vector3(0, -(height / 4), -depth), new Vector3(0, 0, 0), Vector3.Down);
                basicEffect.Projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45), width / height, 0.1f, depth * 2);
            }
        }

        /// <summary>
        /// Reset the projection currently being used. This is based on the CurrentViewPoint.
        /// </summary>
        public void ResetProjection()
        {
            if (currentViewPoint == ViewPoint.TwoD)
            {
                SetBasic2DViewPoint();
            }
            else if (currentViewPoint == ViewPoint.ThreeD)
            {
                SetBasic3DViewPoint();
            }
        }
        #endregion

        #region Public Drawing
        /// <summary>
        /// Adds a single pixel to the graphics. If a pixel on this location already exists, it will be overridden.
        /// </summary>
        /// <param name="location">The X, Y and Z coordinates where the pixel will be added.</param>
        /// <param name="color">The color the added pixel will have.</param>
        public void AddPixel(Vector3 location, Color color)
        {
            RemovePixel(location);

            points.Add(new VertexPositionColor(location, color));
            pointsCheckList.Add(location);
        }

        /// <summary>
        /// Removes a single pixel from the graphics at the specified location.
        /// </summary>
        /// <param name="location">The X, Y and Z coordinates where the pixel will be removed.</param>
        public void RemovePixel(Vector3 location)
        {
            if (pointsCheckList.Contains(location))
            {
                points.RemoveAt(pointsCheckList.IndexOf(location));
                pointsCheckList.Remove(location);
            }
        }

        /// <summary>
        /// Generates a cube that can be correctly drawn with this graphics' cube indexbuffer.
        /// </summary>
        /// <param name="location">The X, Y and Z coordinates of the upper left corner of the cube.</param>
        /// <param name="dimension">The Width, Height and Depth of the cube.</param>
        /// <param name="color">The color of the entire cube.</param>
        /// <returns>A VertexPositionColor array containing the 8 cube points.</returns>
        public VertexPositionColor[] MakeCube(Vector3 location, Vector3 dimension, Color color)
        {
            VertexPositionColor[] cube = new VertexPositionColor[8];
            cube[0] = new VertexPositionColor(new Vector3(location.X, location.Y, location.Z), color);
            cube[1] = new VertexPositionColor(new Vector3(location.X + dimension.X, location.Y, location.Z), color);
            cube[2] = new VertexPositionColor(new Vector3(location.X + dimension.X, location.Y + dimension.Y, location.Z), color);
            cube[3] = new VertexPositionColor(new Vector3(location.X, location.Y + dimension.Y, location.Z), color);
            cube[4] = new VertexPositionColor(new Vector3(location.X, location.Y, location.Z + dimension.Z), color);
            cube[5] = new VertexPositionColor(new Vector3(location.X + dimension.X, location.Y, location.Z + dimension.Z), color);
            cube[6] = new VertexPositionColor(new Vector3(location.X + dimension.X, location.Y + dimension.Y, location.Z + dimension.Z), color);
            cube[7] = new VertexPositionColor(new Vector3(location.X, location.Y + dimension.Y, location.Z + dimension.Z), color);

            return cube;
        }

        /// <summary>
        /// Adds a cube to the graphics at the specified location, with the specified dimension and with the specified
        /// color. If a cube already exists at this location and with this dimension, it will be overridden.
        /// </summary>
        /// <param name="location">The X, Y and Z coordinates of the upper left corner of the cube.</param>
        /// <param name="dimension">The Width, Height and Depth of the cube.</param>
        /// <param name="color">The color of the entire cube.</param>
        public void AddCube(Vector3 location, Vector3 dimension, Color color)
        {
            RemoveCube(location, dimension);

            cubes.Add(MakeCube(location, dimension, color));
            cubesCheckList.Add(new LocationDimensionPair(location, dimension));
        }

        /// <summary>
        /// Adds a custom, user specified, cube to the graphics. The specified VertexPositionColor array must have
        /// exactly 8 entries and must have the same markup as specified by the cube indexbuffer.
        /// 
        /// Cube format required:
        ///   4 --- 5
        ///  /|    /|
        /// 0 +-- 1 |
        /// | 7 --+ 6
        /// |/    |/
        /// 3 --- 2
        /// </summary>
        /// <param name="cube">The VertexPositionColor array containing the 8 cube points.</param>
        /// <exception cref="ArgumentException">
        /// This is thrown when the cube array does not contain 8 points, or
        /// when the cube points don't match with the required format.
        /// </exception>
        public void AddCustomCube(VertexPositionColor[] cube)
        {
            if (cube.Length != 7)
            {
                throw new ArgumentException("This is not a cube!");
            }

            float cX = cube[0].Position.X;
            float cY = cube[0].Position.Y;
            float cZ = cube[0].Position.Z;
            float cWidth = cX - cube[7].Position.X;
            float cHeight = cY - cube[7].Position.X;
            float cDepth = cZ - cube[7].Position.X;

            if (cube[1].Position != new Vector3(cX + cWidth, cY, cZ) ||
                 cube[2].Position != new Vector3(cX + cWidth, cY + cHeight, cZ) ||
                 cube[3].Position != new Vector3(cX, cY + cHeight, cZ) ||
                 cube[4].Position != new Vector3(cX, cY, cZ + cDepth) ||
                 cube[5].Position != new Vector3(cX + cWidth, cY, cZ + cDepth) ||
                 cube[6].Position != new Vector3(cX + cWidth, cY + cHeight, cZ + cDepth))
            {
                throw new ArgumentException("The cube is not properly formatted!");
            }

            Vector3 location = new Vector3(cX, cY, cZ);
            Vector3 dimension = new Vector3(cWidth, cHeight, cDepth);

            RemoveCube(location, dimension);

            cubes.Add(cube);
            cubesCheckList.Add(new LocationDimensionPair(location, dimension));
        }

        /// <summary>
        /// Removes a cube from the graphics which has the specified location and dimension. Color is ignored.
        /// </summary>
        /// <param name="location">The X, Y and Z location of the top left corner of the cube.</param>
        /// <param name="dimension">The Width, Height and Depth of the cube to be removed.</param>
        public void RemoveCube(Vector3 location, Vector3 dimension)
        {
            LocationDimensionPair locationDimensionPair = new LocationDimensionPair(location, dimension);

            if (cubesCheckList.Contains(locationDimensionPair))
            {
                cubes.RemoveAt(cubesCheckList.IndexOf(locationDimensionPair));
                cubesCheckList.Remove(locationDimensionPair);
            }
        }

        /// <summary>
        /// Generates a pyramid that can be correctly drawn with this graphics' pyramid indexbuffer.
        /// 
        /// <remarks>
        /// NB: the location specifies the upper left corner of the pyramid, which does not have to be an
        /// actual point of the pyramid collection.
        /// </remarks>
        /// </summary>
        /// <param name="location">The X, Y and Z coordinates of the upper left corner of the pyramid.</param>
        /// <param name="dimension">The Width, Height and Depth of the pyramid.</param>
        /// <param name="color">The color of the entire pyramid.</param>
        /// <returns>A VertexPositionColor array containing the 5 pyramid points.</returns>
        public VertexPositionColor[] MakePyramid(Vector3 location, Vector3 dimension, Color color)
        {
            VertexPositionColor[] pyramid = new VertexPositionColor[5];
            pyramid[0] = new VertexPositionColor(new Vector3(location.X, location.Y + dimension.Y, location.Z), color);
            pyramid[1] = new VertexPositionColor(new Vector3(location.X + dimension.X, location.Y + dimension.Y, location.Z), color);
            pyramid[2] = new VertexPositionColor(new Vector3(location.X + dimension.X, location.Y + dimension.Y, location.Z + dimension.Z), color);
            pyramid[3] = new VertexPositionColor(new Vector3(location.X, location.Y + dimension.Y, location.Z + dimension.Z), color);
            pyramid[4] = new VertexPositionColor(new Vector3(location.X + (dimension.X / 2), location.Y, location.Z + (dimension.Z / 2)), color);

            return pyramid;
        }

        /// <summary>
        /// Adds a pyramid to the graphics at the specified location, with the specified dimension and with the specified
        /// color. If a pyramid already exists at this location and with this dimension, it will be overridden.
        /// </summary>
        /// <param name="location">The X, Y and Z coordinates of the upper left corner of the pyramid.</param>
        /// <param name="dimension">The Width, Height and Depth of the pyramid.</param>
        /// <param name="color">The color of the entire pyramid.</param>
        public void AddPyramid(Vector3 location, Vector3 dimension, Color color)
        {
            RemovePyramid(location, dimension);

            pyramids.Add(MakePyramid(location, dimension, color));
            pyramidsCheckList.Add(new LocationDimensionPair(location, dimension));
        }

        /// <summary>
        /// Adds a custom, user specified, pyramid to the graphics. The specified VertexPositionColor array must have
        /// exactly 5 entries and must have the same markup as specified by the pyramid indexbuffer.
        /// 
        /// Pyramid format required (0 --- 1 is the front of the pyramid):
        ///      4
        ///     / \
        ///    /   \
        ///   3 --- 2
        ///  /     /
        /// 0 --- 1
        /// </summary>
        /// <param name="pyramid">The VertexPositionColor array containing the 8 cube points.</param>
        /// <exception cref="ArgumentException">
        /// This is thrown when the pyramid array does not contain 5 points, or
        /// when the pyramid points don't match with the required format.
        /// </exception>
        public void AddCustomPyramid(VertexPositionColor[] pyramid)
        {
            if (pyramid.Length != 5)
            {
                throw new ArgumentException("This is not a pyramid!");
            }

            float pWidth = (pyramid[2].Position.X - pyramid[4].Position.X) * 2;
            float pHeight = (pyramid[2].Position.Y - pyramid[4].Position.Y);
            float pDepth = (pyramid[2].Position.Z - pyramid[4].Position.Z) * 2;
            float pX = pyramid[4].Position.X - (pWidth / 2);
            float pY = pyramid[2].Position.Y;
            float pZ = pyramid[4].Position.Z - (pDepth / 2);

            if (pyramid[0].Position != new Vector3(pX, pY + pHeight, pZ) ||
                 pyramid[1].Position != new Vector3(pX + pWidth, pY + pHeight, pZ) ||
                 pyramid[3].Position != new Vector3(pX, pY + pHeight, pZ + pDepth))
            {
                throw new ArgumentException("The pyramid is not properly formatted!");
            }

            Vector3 location = new Vector3(pX, pY, pZ);
            Vector3 dimension = new Vector3(pWidth, pHeight, pDepth);

            RemoveCube(location, dimension);

            cubes.Add(pyramid);
            cubesCheckList.Add(new LocationDimensionPair(location, dimension));
        }

        /// <summary>
        /// Removes a pyramid from the graphics which has the specified location and dimension. Color is ignored.
        /// </summary>
        /// <param name="location">The X, Y and Z location of the top left corner of the pyramid.</param>
        /// <param name="dimension">The Width, Height and Depth of the pyramid to be removed.</param>
        public void RemovePyramid(Vector3 location, Vector3 dimension)
        {
            LocationDimensionPair locationDimensionPair = new LocationDimensionPair(location, dimension);

            if (pyramidsCheckList.Contains(locationDimensionPair))
            {
                pyramids.RemoveAt(pyramidsCheckList.IndexOf(locationDimensionPair));
                pyramidsCheckList.Remove(locationDimensionPair);
            }
        }
        
        /// <summary>
        /// Invalidates the backbuffer with a standard background color. All graphical objects will be redrawn.
        /// </summary>
        public void Invalidate()
        {
            Invalidate(Color.CornflowerBlue);
        }

        /// <summary>
        /// Invalidates the backbuffer with the given color. All graphical objects will be redrawn.
        /// </summary>
        /// <param name="backgroundColor">The background color to which to clear the backbuffer.</param>
        public void Invalidate(Color backgroundColor)
        {
            GraphicsDevice.Clear(backgroundColor);

            DrawPoints();
            DrawCubes();
            DrawPyramids();

            Present();
        }

        /// <summary>
        /// Presents the contents of the backbuffer to the screen. Use this when nothing needs to be
        /// redrawn, but the screen does have to be refreshed.
        /// </summary>
        public void Present()
        {
            // Step 11 (Display everything in the backbuffer).
            GraphicsDevice.Present();
        }

        /// <summary>
        /// Clears the graphics of all graphical objects.
        /// </summary>
        public void Clear()
        {
            points = new List<VertexPositionColor>();
            pointsCheckList = new List<Vector3>();
            cubes = new List<VertexPositionColor[]>();
            cubesCheckList = new List<LocationDimensionPair>();
            pyramids = new List<VertexPositionColor[]>();
            pyramidsCheckList = new List<LocationDimensionPair>();
        }
        #endregion

        #region Private Drawing
        /// <summary>
        /// Draws all points in the graphics object, if there is at least 1 point.
        /// </summary>
        private void DrawPoints()
        {
            if (points.Count > 0)
            {
                // Step 1-3 (Choose and create an array of vertices and fill them).
                VertexPositionColor[] pointVertices = points.ToArray();
                // Step 4 (Create a VertexBuffer).
                VertexBuffer pointBuffer = new VertexBuffer(GraphicsDevice, pointVertices.Length * VertexPositionColor.SizeInBytes, BufferUsage.None);
                // Step 5 (Fill the VertexBuffer with the vertices).
                pointBuffer.SetData<VertexPositionColor>(pointVertices);
                // Step 6 (Set the GraphicsDevice to the proper Vertex Format).
                GraphicsDevice.VertexDeclaration = new VertexDeclaration(GraphicsDevice, VertexPositionColor.VertexElements);
                // Step 7 (Connect an unused VertexStream of the GraphicsDevice to the VertexBuffer).
                GraphicsDevice.Vertices[0].SetSource(pointBuffer, 0, VertexPositionColor.SizeInBytes);
                // Step 8.1 (Start using a chosen effect, and stop using the effect after the drawing).
                basicEffect.Begin();
                // Step 9 (Provide additional information to the effect).
                basicEffect.VertexColorEnabled = true;
                // Step 10 (Use the effect to draw the primitive types).
                foreach (EffectPass effectPass in basicEffect.CurrentTechnique.Passes)
                {
                    effectPass.Begin();
                    GraphicsDevice.DrawPrimitives(pointDrawStyle, 0, pointVertices.Length);
                    effectPass.End();
                }
                // Step 8.2 (Stop using the chosen effect).
                basicEffect.End();
            }
        }

        /// <summary>
        /// Draws all cubes in the graphics object using the static cube indexbuffer.
        /// </summary>
        private void DrawCubes()
        {
            // Step 1-3 (Choose and create an array of vertices and fill them).
            foreach (VertexPositionColor[] cube in cubes)
            {
                // Step 4 (Create a VertexBuffer).
                VertexBuffer cubeBuffer = new VertexBuffer(GraphicsDevice, cube.Length * VertexPositionColor.SizeInBytes, BufferUsage.None);
                // Step 5 (Fill the VertexBuffer with the vertices).
                cubeBuffer.SetData<VertexPositionColor>(cube);
                // Step 6 (Set the GraphicsDevice to the proper Vertex Format).
                GraphicsDevice.VertexDeclaration = new VertexDeclaration(GraphicsDevice, VertexPositionColor.VertexElements);
                // Step 7.1 (Connect an unused VertexStream of the GraphicsDevice to the VertexBuffer).
                GraphicsDevice.Vertices[0].SetSource(cubeBuffer, 0, VertexPositionColor.SizeInBytes);
                // Step 7.2 (Due to cube drawing, set the indices which tell the device what points to connect to what other points to make triangles).
                GraphicsDevice.Indices = cubeIndexBuffer;
                // Step 8.1 (Start using a chosen effect, and stop using the effect after the drawing).
                basicEffect.Begin();
                // Step 9 (Provide additional information to the effect).
                basicEffect.VertexColorEnabled = true;
                // Step 10 (Use the effect to draw the primitive types).
                foreach (EffectPass effectPass in basicEffect.CurrentTechnique.Passes)
                {
                    effectPass.Begin();
                    GraphicsDevice.DrawIndexedPrimitives(cubeDrawStyle, 0, 0, cube.Length, 0, 12);
                    effectPass.End();
                }
                // Step 8.2 (Stop using the chosen effect).
                basicEffect.End();
            }
        }

        /// <summary>
        /// Draws all pyramids in the graphics object using the static pyramid indexbuffer.
        /// </summary>
        private void DrawPyramids()
        {
            // Step 1-3 (Choose and create an array of vertices and fill them).
            foreach (VertexPositionColor[] pyramid in pyramids)
            {
                // Step 4 (Create a VertexBuffer).
                VertexBuffer pyramidBuffer = new VertexBuffer(GraphicsDevice, pyramid.Length * VertexPositionColor.SizeInBytes, BufferUsage.None);
                // Step 5 (Fill the VertexBuffer with the vertices).
                pyramidBuffer.SetData<VertexPositionColor>(pyramid);
                // Step 6 (Set the GraphicsDevice to the proper Vertex Format).
                GraphicsDevice.VertexDeclaration = new VertexDeclaration(GraphicsDevice, VertexPositionColor.VertexElements);
                // Step 7.1 (Connect an unused VertexStream of the GraphicsDevice to the VertexBuffer).
                GraphicsDevice.Vertices[0].SetSource(pyramidBuffer, 0, VertexPositionColor.SizeInBytes);
                // Step 7.2 (Due to pyramid drawing, set the indices which tell the device what points to connect to what other points to make triangles).
                GraphicsDevice.Indices = pyramidIndexBuffer;
                // Step 8.1 (Start using a chosen effect, and stop using the effect after the drawing).
                basicEffect.Begin();
                // Step 9 (Provide additional information to the effect).
                basicEffect.VertexColorEnabled = true;
                // Step 10 (Use the effect to draw the primitive types).
                foreach (EffectPass effectPass in basicEffect.CurrentTechnique.Passes)
                {
                    effectPass.Begin();
                    GraphicsDevice.DrawIndexedPrimitives(pyramidDrawStyle, 0, 0, pyramid.Length, 0, 8);
                    effectPass.End();
                }
                // Step 8.2 (Stop using the chosen effect).
                basicEffect.End();
            }
        }
        #endregion
    }
}